#coding=utf-8

import sys
import logging
import re
import time
import urllib
import random
import uuid

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.api import memcache

from utils import util
from utils import page

COUNTER_NAME_TOTAL_ENTRY_COUNT = 'total_entry_count'
_SHARDS_PER_COUNTER = 20

gen_key_name = lambda s: 'kn' + uuid.uuid3(uuid.NAMESPACE_URL, util.encode_str(s)).get_hex()

#http://feeds.feedburner.com/~r/AppEngineFan/~3/310349458/efficient-global-counters.html
class ShardCounter(db.Model):
    name = db.StringProperty(required=True)
    count = db.IntegerProperty(default=0)
    
    @classmethod
    def get_count(cls, nameOfCounter):
        memcache_id = '/ShardCounter/%s' %  nameOfCounter
        result = memcache.get(memcache_id)
        if not (result == None):
            return result
        result = 0
        for shard in cls.gql('WHERE name=:1', nameOfCounter):
            result += shard.count
        memcache.set(memcache_id, result, 60)
        return result
    @classmethod
    def update_count(cls, nameOfCounter, delta):
        memcache_id = '/ShardCounter/%s' %  nameOfCounter
        memcache.delete(memcache_id)
        shard_id = '/%s/%s' % (nameOfCounter, random.randint(1, _SHARDS_PER_COUNTER))
        def update():
            shard = cls.get_by_key_name(shard_id)
            if shard:
                shard.count += delta
            else:
                shard = cls(key_name=shard_id, name=nameOfCounter, count=delta)
            shard.put()
        db.run_in_transaction(update)

class Account(db.Model):
    
    user         = db.UserProperty(required=True)
    email        = db.EmailProperty(required=True)  # key == <email>
    nickname     = db.StringProperty()
    created      = db.DateTimeProperty(auto_now_add=True)
    modified     = db.DateTimeProperty(auto_now=True)
    access_count = db.IntegerProperty(default = 0)

    @classmethod
    def get_account_for_user(cls, user):
        """Get the Account for a user, creating a default one if needed."""
        email = user.email()
        assert email
        key = '<%s>' % email
        nickname = user.nickname()
        if '@' in nickname:
            nickname = nickname.split('@', 1)[0]
        assert nickname
        return cls.get_or_insert(key, user=user, email=email, nickname=nickname)

    @classmethod
    def get_account_for_email(cls, email):
        """Get the Account for an email address, or return None."""
        assert email
        key = '<%s>' % email
        return cls.get_by_key_name(key)
    
    @classmethod
    def get_current_account(cls):
        usr = users.get_current_user()
        return Account.get_account_for_user(usr)
    
    @property
    def is_admin(self):
        return users.is_current_user_admin()
    
    @property
    def is_sign_in(self):
        return not self.email == 'anonymous@gmail.com' 

class Entry(db.Model):
    
    title          = db.StringProperty()
    link           = db.StringProperty()
    summary        = db.TextProperty()
    text           = db.TextProperty()
    author         = db.StringProperty()
    pub_time       = db.DateTimeProperty(auto_now_add=True)
    update_time    = db.DateTimeProperty(auto_now=True)
    allow_comments = db.BooleanProperty(default=True)
    tags_as_string    = db.StringProperty()
    tag_key_list   = db.ListProperty(db.Key)
    
    def save(self):
        logging.debug('new tags are: ' + self.tags_as_string)
        
        for tk in self.tag_key_list:
            Entry.update_tag_by_step(tk, -1)
        
        new_tag_list = [t for t in re.split('[\s,]+', self.tags_as_string) if t]
        self.tag_key_list = [Tag.get_or_insert(gen_key_name(tag_name), name = tag_name).key() for tag_name in new_tag_list]
        
        for tk in self.tag_key_list:
            Entry.update_tag_by_step(tk, 1)
        
        self.put()
    
    def create(self):
        self.save()
        #总数计数器增1
        ShardCounter.update_count(COUNTER_NAME_TOTAL_ENTRY_COUNT, 1)
    
    def update(self):
        self.save()
    
    def remove(self):
        for tk in self.tag_key_list:
            Entry.update_tag_by_step(tk, -1)
        self.delete()
        #总数计数器减1
        ShardCounter.update_count(COUNTER_NAME_TOTAL_ENTRY_COUNT, -1)
    
    @classmethod
    def get_page(cls, page_num, page_size = page.DEFAULT_PAGE_SIZE):
        offset = (page_num - 1) * page_size
        entries = cls.gql('ORDER BY pub_time DESC').fetch(page_size, offset)
        entry_count = ShardCounter.get_count(COUNTER_NAME_TOTAL_ENTRY_COUNT)
        logging.debug('entry count: ' + str(entry_count))
        return page.Page(entries, page_num, entry_count, page_size)
    
    @classmethod
    def get_feed_entries(cls):
        entries = cls.gql('ORDER BY pub_time DESC').fetch(page.DEFAULT_PAGE_SIZE)
        return entries
    
    @staticmethod
    def update_tag_by_step(tk, step):
        tag = db.get(tk)
        if tag is None:
            return
        tag.entry_count += step
        if tag.entry_count <= 0:
            tag.delete()
        else:
            tag.put()
    
    @property
    def id(self):
        return self.key().id()
        
    @property
    def local_put_time(self):
        return util.get_local_time(self.pub_time).strftime('%Y-%m-%d %H:%M')
    
    @property
    def content_summary(self):
        logging.debug('begin to generate summary')
        summary = util.get_summary(self.text)
        return summary
    
    @property
    def entry_tags(self):
        return [db.get(tk) for tk in self.tag_key_list]
    
    @property 
    def tags_string(self):
        return ','.join([t.name for t in self.entry_tags])
    
    @property
    def comment_num(self):
        return len(self.entry_comments)
    
    @property
    def has_comment(self):
        return len(self.entry_comments) > 0
    

class Comment(db.Model):
    name         = db.StringProperty(default='')
    email        = db.StringProperty()
    url          = db.StringProperty(default='')
    content      = db.TextProperty()
    post_time    = db.DateTimeProperty(auto_now_add=True)
    status       = db.StringProperty()
    notify       = db.BooleanProperty(default=False)
    remote_host  = db.StringProperty()
    referer      = db.StringProperty()
    user_agent   = db.StringProperty()
    
    weblog_entry = db.ReferenceProperty(Entry, collection_name='entry_comments')
    
    def create(self):
        self.put()
    
    def update(self):
        self.put()
    
    def remove(self):
        self.delete()
    
    @property
    def local_put_time(self):
        return util.get_local_time(self.post_time).strftime('%Y-%m-%d %H:%M')

class Tag(db.Model):
    name        = db.StringProperty(required=True)
    add_time    = db.DateTimeProperty(auto_now_add = True)
    update_time = db.DateTimeProperty(auto_now=True)
    entry_count = db.IntegerProperty(default = 0)       #标签所包含的文章数，初始值是0
    
    #值为显示标签时的css值
    @property
    def weight(self):
        if self.entry_count >= 4:
            return 3
        elif self.entry_count <= 4 and self.entry_count >=2:
            return 4
        else:
            return 5
    
    @property
    def key_name(self):
        return self.key().name()
    
    @property
    def entries(self):
        return Entry.gql('WHERE tag_key_list = :1', self.key())
    
    @classmethod
    def all_entry_tags(cls):
        return cls.gql('WHERE entry_count > 0')